#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <mutex>
#include <cassert>
#include <fstream>
#include <jsoncpp/json/json.h>
#include "debug.h"
#if MYSQLMODEL
#include "oj_model2.hpp"
#else
#include "oj_model.hpp"
#endif

#include "oj_view.hpp"

#include "../common/httplib.h"
#include "../common/util.hpp"
#include "../common/log.hpp"

namespace ns_control{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_model;
    using namespace ns_view;
    using namespace httplib;

    // 提供服务的主机
    class Machine{
    public:
        Machine():ip(""),port(0),load(0),mtx(nullptr){

        }
        ~Machine(){

        }
        void IncLoad(){
            if(mtx)
            mtx->lock();
            load++;
            if(mtx)
            mtx->unlock();
        }
        void DecLoad(){
            if(mtx)
            mtx->lock();
            load--;
            if(mtx)
            mtx->unlock();
        }
        void ResetLoad()
        {
            if(mtx) mtx->lock();
            load = 0;
            if(mtx) mtx->unlock();
        }
        uint64_t Load(){
            uint64_t _load = 0;
            if(mtx)
            mtx->lock();
            _load = load;
            if(mtx)
            mtx->unlock();
            return _load;
        }
    public:
        std::string ip;
        int port;
        uint64_t load; // 负载情况
        std::mutex* mtx;//mutex禁止拷贝,需要使用指针
    };
    const std::string service_machine = "./conf/service_machine.conf";
    // 负载均衡模块
    class LoadBlance{
    public:
        LoadBlance(){
            assert(LoadConf(service_machine));
            LOG(INFO)<<" 加载 "<<service_machine<<"成功"<<'\n';
        }
        ~LoadBlance(){
            
        }
        bool LoadConf(const std::string& machine_conf){
            std::ifstream in(machine_conf);
            if(!in.is_open()){
                LOG(FATAL)<<" 加载："<<machine_conf<<" 失败"<<'\n';
                return false;
            }
            std::string line;
            while(getline(in,line)){
                std::vector<std::string> tokens;
                StringUtil::SplitString(line,&tokens,":");
                if(tokens.size()!=2){
                    LOG(WARNING)<<" 切分 "<<line<<" 失败"<<'\n';
                    continue;;
                }
                Machine m;
                m.ip = tokens[0];
                m.port = atoi(tokens[1].c_str());
                m.load = 0;
                m.mtx = new std::mutex();

                online.push_back(machines.size());
                machines.push_back(m);
            }
            in.close();
            return true;
        }
        // id:输出型参数
        bool SmartChioce(int* id,Machine** m){
            // 1. 使用选择好的主机（更新主机的负载）
            // 2. 我们需要可能离线该主机
            mtx.lock();
            // 负载均衡的算法
            //      1）随机数 + hash
            //      2）轮询 + hash
            int online_num = online.size();
            if(online_num == 0){
                LOG(FATAL)<<" 所有的后端编译主机全部离线,请运维的同时尽快查看"<<'\n';
                mtx.unlock();
                return false;
            }
            // 通过遍历的方式,找到负载最小的机器
            *id = online[0];
            *m = &machines[online[0]];
            uint64_t min_load = machines[online[0]].Load();
            for(int i = 0;i<online_num;i++){
                uint64_t cur_load = machines[online[i]].Load();
                if(min_load > cur_load){
                    min_load = cur_load;
                    *id = online[i];
                    *m = &machines[online[i]];
                }
            }
            mtx.unlock();
            return true;
        }
        void OfflineMachine(int which){
            mtx.lock();
            auto it = online.begin();
            while(it!=online.end()){
                if(*it==which){
                    // 找到了
                    machines[which].ResetLoad();
                    offline.push_back(*it);
                    online.erase(it);
                    break;
                }
                it++;
            }
            mtx.unlock();
        }
        // 全部上线
        void OnlineMachine(){
            mtx.lock();
            online.insert(online.end(),offline.begin(),offline.end());
            offline.erase(offline.begin(),offline.end());
            mtx.unlock();
            LOG(INFO)<<" 所有的主机上线了"<<'\n';
        }
        // for test 仅做测试使用
        void ShowMachines(){
            mtx.lock();
            std::cout<<"当前在线主机列表：";
            for(auto&id:online){
                std::cout<<id<<' ';
            }
            std::cout<<'\n';
            std::cout<<"当前离线主机列表：";
            for(auto&id:offline){
                std::cout<<id<<' ';
            }
            std::cout<<'\n';
            mtx.unlock();
        }
    private:
        // 可以提供编译服务的所有的主机
        // 每一台主机都有自己的下标，充当当前主机的id
        std::vector<Machine> machines; 
        // 所有在线的主机ID
        std::vector<int> online;
        // 所有离线的主机ID
        std::vector<int> offline;
        // 保证LoadBlance数据安全
        std::mutex mtx;
    };
    // 控制器
    class Control{
    public:
        Control(){

        }
        ~Control(){

        }
        void RecoveryMachine(){
            load_blance_.OnlineMachine();
        }
        // 根据题目数据构建网页
        bool AllQuestions(std::string* html){
            bool ret = true;
            std::vector<ns_model::Question> all;
            if(model_.GetAllQuestions(&all)){
                // 获取题目信息成功，将所有的题目数据构建成网页
                view_.AllExpandHtml(all,html);
            }
            else{
                *html = "获取题目失败,形成题目列表失败";
                ret = false;
            }
            return ret;
        }
        bool Question(const std::string& number,std::string* html){
            bool ret = true;
            ns_model::Question q;
            if(model_.GetOneQuestion(number,&q)){
                // 获取指定题目信息成功,将所有的题目数据构建成网页
                view_.OneExpandHtml(q,html);
            }
            else{
                *html = "指定题目："+number+" 不存在";
                ret = false;
            }
            return ret;
        }
        void Judge(const std::string&number,const std::string& in_json,std::string* out_json){
            // code: #include 
            // input: 
            
            // 0. 根据题目编号，直接拿到对应的题目细节
            ns_model::Question q;
            model_.GetOneQuestion(number,&q);
            // 1. 对in_json记性反序列化，得到id和用户提交的代码
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json,in_value);
            std::string code = in_value["code"].asString();
            // 2. 重新拼接用户代码+测试用例，形成新的代码
            Json::Value compile_value;
            compile_value["input"] = in_value["input"].asString();
            compile_value["code"] = code + "\n" + q.tail;
            compile_value["cpu_limit"] = q.cpu_limit;
            compile_value["mem_limit"] = q.mem_limit;
            Json::FastWriter writer;
            std::string compil_string = writer.write(compile_value);
            // 3. 负载均衡，选择负载最低的主机
            // 一直选择，直到编译主机可用，否则就全部挂掉
            while(true){
                int id = 0;
                Machine* m =nullptr;
                if(!load_blance_.SmartChioce(&id,&m)){
                    break;
                }
                m->IncLoad();
                LOG(INFO)<<" 选择主机成功,主机ID为 "
                <<id<<" 详情："<<m->ip<<":"<<m->port<<" 当前主机的负载是: "<<m->Load()<<'\n';

                // 4. 发起http请求，得到结果
                Client cli(m->ip,m->port);
                auto res = cli.Post("/compile_and_run",compil_string,"application/json; charset=utf-8");
                if(res){
                    // 5. 将结果给out_json
                    if(res->status==200){
                        *out_json = res->body;
                        // m->DecLoad();
                        LOG(INFO)<<" 请求编译和运行成功"<<'\n';
                        break;
                    }
                    m->DecLoad();
                }
                else{
                    // 请求失败
                    LOG(ERROR)<<" 当前请求的主机,ID为 "
                    <<id<<" 详情："<<m->ip<<":"<<m->port<<"可能已经离线"<<'\n';
                    m->IncLoad();
                    load_blance_.OfflineMachine(id);
                    load_blance_.ShowMachines(); // 仅仅是用来调试
                }
            }
            

            

        }
    private:
        Model model_; // 提供后台数据
        View view_; // 提供html渲染功能
        LoadBlance load_blance_;// 核心负载均衡器
    };
}